import java.util.Collections;
import java.util.Stack;
import java.util.ArrayList;

public class SymTable {
	
	private int blockLevel; //actual nivel de profundidad
	private Stack<EntryTS> ts; //tabla de simbolos
	private ArrayList<EntryTR> tr; 	//RwTable tr; //tabla de palabras reservadas
	
	public SymTable() { //inicializacion
		init();
	}
	
	//funciones de mas bajo nivel:
	
	public EntryTS insert(String name, int kind, long value, int tipo) {
		EntryTS entrada = new EntryTS(name, kind, this.getBlockLevel(), value, tipo);
		ts.push(entrada);
		return entrada;
	}
	
	public EntryTS find(String name) {
		for (EntryTS entrada : ts) { //busca en tabla de simbolos
			if (entrada.getName().equals(name))
				return entrada;
		}
		
		return null; //no encontrado. EntryTS() nula
	}
	
	public EntryTS modify(String name, int kind, long value, int tipo) {
		int i;
	    for (i = 0; i < ts.size(); i++)
	    	if (ts.get(i).getName().equals(name)) {//si es la entrada encontrada
	    		EntryTS entrada = ts.get(i);
	    		entrada.setKind(kind);
	    		entrada.setValue(value);
	    		entrada.setTipo(tipo);
	    		ts.set(i, entrada);
	    		return entrada;
	    	}
	    	
		return null; //no encontrada
	}
	
	public EntryTS modifyValue(String name, long value) {
		int i;
	    for (i = 0; i < ts.size(); i++)
	    	if (ts.get(i).getName().equals(name)) {//si es la entrada encontrada
	    		EntryTS entrada = ts.get(i);
	    		entrada.setValue(value);
	    		ts.set(i, entrada);
	    		return entrada;
	    	}
	    	
		return null; //no encontrada
	}
	
	/*public long findValue(String name) {
		
	}*/
	
	public EntryTS lookUp(String name) {
		EntryTS entrada = find(name);
		if (entrada == null) { //no encontrado. getKind() == EntryTS.NIL
			//entrada = new EntryTS(name, EntryTS.UDEC, this.getBlockLevel(), (long) 0);
			entrada = insert(name, EntryTS.UDEC, 0, 0);
		}
		
		return entrada; //en cualquier caso devuelve entrada
	}
	
	public EntryTS lookUp(String name, int kind, long value, int tipo) {
		EntryTS entrada = find(name);
		if (entrada == null)  //no encontrado. getKind() == EntryTS.NIL
			entrada = insert(name, kind, value, tipo);

		
		return entrada; //en cualquier caso devuelve entrada
	}
	
	public int binary(String name) { //busca en tabla reservadas y si no lookUp. devuelve token
	    //TODO busqueda binaria
		return Tokens.ID; //no encontrado en reservadas, si no en TS
	}
	
	//funciones de mas alto nivel:
	public void createLocal(String name) {
		insert(name, EntryTS.UDEC, 0, 0);
	}
	
	public void init() {
		this.setBlockLevel(0); //empieza bloque global
		ts = new Stack<EntryTS>();
		tr = new ArrayList<EntryTR>();
		
		insert("main", EntryTS.UDEC, 0, 0); //inserta funcion main en bloque global
		
		//inicializacion de palabras reservadas
		tr.add(new EntryTR("break", Tokens.BREAK));
		tr.add(new EntryTR("continue", Tokens.CONTINUE));
		tr.add(new EntryTR("else", Tokens.ELSE));
		tr.add(new EntryTR("if", Tokens.IF));
		tr.add(new EntryTR("int", Tokens.INT));
		tr.add(new EntryTR("long", Tokens.LONG));
		tr.add(new EntryTR("while", Tokens.WHILE));
		tr.add(new EntryTR("return", Tokens.RETURN));
		//Collections.sort(tr); //ordenada reservadas para hacer luego busqueda binaria
	}
	
	public void move() {
		
	}
	
	public void blkPush() {
		this.blockLevel++;
	}
	
	public void blkPop() {
		/*for (EntryTS entrada : ts) {
			if (entrada.getBlockLevel() == this.getBlockLevel()) //elimina todo lo del nivel actual
				//ts.remove(entrada);
				ts.pop();
		}*/
		
		this.blockLevel--;
	}

	//otros metodos:
	
	@Override
	public String toString() {
		String cadena = "SymTable [blockLevel=" + blockLevel + ", ts= \n";
		for (EntryTS entrada : ts)
			cadena += entrada + "\n";
		return cadena;
	}


	public int getBlockLevel() {
		return blockLevel;
	}


	public void setBlockLevel(int blockLevel) {
		this.blockLevel = blockLevel;
	}
		
}
